package com.hibase.upms.service.menu.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.github.pagehelper.PageInfo;
import com.google.common.collect.Lists;
import com.hibase.common.entity.User;
import com.hibase.common.exception.HibaseAssert;
import com.hibase.common.util.EntityUtil;
import com.hibase.common.util.UserContext;
import com.hibase.core.mybatis.service.BaseServiceImpl;
import com.hibase.core.web.excel.ExcelParse;
import com.hibase.upms.constant.MdsMenuConstant;
import com.hibase.upms.constant.MenuManagerConstant;
import com.hibase.upms.constant.UserManagerConstant;
import com.hibase.upms.entity.menu.ImportMenuVO;
import com.hibase.upms.entity.menu.MdsMenu;
import com.hibase.upms.entity.menu.dv.MdsDataPermDV;
import com.hibase.upms.entity.menu.dv.MdsMenuRoleDV;
import com.hibase.upms.entity.menu.vo.MdsMenuIndexVO;
import com.hibase.upms.entity.menu.vo.MdsMenuSearchVO;
import com.hibase.upms.entity.menu.vo.MdsMenuTreeVO;
import com.hibase.upms.entity.menu.vo.MdsMenuUpdateVO;
import com.hibase.upms.entity.rolemenu.MdsRoleMenu;
import com.hibase.upms.mapper.menu.MdsMenuMapper;
import com.hibase.upms.service.menu.MdsMenuService;
import com.hibase.upms.service.rolemenu.MdsRoleMenuService;
import com.hibase.upms.service.userrole.MdsUserRoleService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author hufeng
 * @since 2019-04-03
 */
@Service
@Slf4j
@Transactional(rollbackFor = Exception.class,readOnly = true)
@RequiredArgsConstructor
public class MdsMenuServiceImpl extends BaseServiceImpl<MdsMenuMapper, MdsMenu> implements MdsMenuService {

    private final MdsUserRoleService userRoleService;

    private final MdsRoleMenuService mdsRoleMenuService;

    @Override
    public List<MdsMenu> selectList() {
        //1,查询当前用户所分配的角色id
        List<String> roleIds = this.selectRoleIdByCurrentUser();

        if (ObjectUtils.isNull(roleIds)) {

            return new ArrayList<>();
        }

        // 查询所有的菜单节点
        List<MdsMenu> rootList = this.selectRootList(roleIds, null);

        return rootList;
    }

    @Override
    public List<MdsMenu> selectPDAList() {

        //1,查询当前用户所分配的角色id
        List<String> roleIds = this.selectRoleIdByCurrentUser();

        if (ObjectUtils.isNull(roleIds)) {

            return new ArrayList<>();
        }

        // 查询所有的菜单节点
        List<MdsMenu> rootList = this.selectRootList(roleIds, "PDA");

        return rootList;
    }

    /**
     * 查询当前用户所分配的角色id
     */
    private List<String> selectRoleIdByCurrentUser() {
        // 获取用户信息
        User user = UserContext.get();
        return userRoleService.selectRoleIdByCurrentUser(user.getId());
    }

    /**
     * 查询所有的菜单节点
     */
    private List<MdsMenu> selectRootList(List<String> roleIds, String pageType) {

        List<MdsMenu> result = new ArrayList<>();

        Map<String, Object> map = new HashMap<>();

        map.put("roleId", roleIds);
        map.put("pageType", pageType);
        List<MdsMenu> mdsMenus = super.getBaseMapper().selectRootList(map);

        if (CollUtil.isNotEmpty(mdsMenus)) {

            for (MdsMenu mdsMenu : mdsMenus) {

                if (StrUtil.isBlank(mdsMenu.getParentId())) {

                    mdsMenu.setParentId(UserManagerConstant.MENU_TYPE_ROOT);
                }

                // 查找根目录下的菜单
                if (UserManagerConstant.MENU_TYPE_ROOT.equals(mdsMenu.getParentId())) {

                    result.add(mdsMenu);
                }
            }

            //为一级菜单设置子菜单准备递归
            for (MdsMenu menuBO : result) {

                //获取父菜单下所有子菜单调用recursionMenu
                List<MdsMenu> childList = recursionMenu(menuBO.getId(), mdsMenus);
                menuBO.setMenuBODetail(childList);
            }
        }

        return result;
    }

    public List<MdsMenu> recursionMenu(String id, List<MdsMenu> menuBOList) {

        //构建子菜单
        List<MdsMenu> childList = new ArrayList<>();

        //遍历传入的list
        for (MdsMenu menuBO : menuBOList) {

            //所有菜单的父id与传入的根节点id比较，若相等则为该级菜单的子菜单
            if (menuBO.getParentId().equals(id)) {

                childList.add(menuBO);
            }
        }

        //递归
        for (MdsMenu m : childList) {

            m.setMenuBODetail(recursionMenu(m.getId(), menuBOList));
        }

        if (CollUtil.isEmpty(childList)) {

            return new ArrayList<MdsMenu>();
        }
        // id 转换为code
        //childList.forEach(p->p.setId(p.getCode()));
        return childList;
    }

    /**
     * 查询角色-菜单关系
     */
    @Override
    public List<MdsMenuRoleDV> selectRoleAndPathInfos() {

        return super.baseMapper.selectRoleAndPathInfos();
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = false)
    public void changeMenu(MdsMenu mdsMenu) {
        if (StrUtil.isBlank(mdsMenu.getId())) {
            mdsMenu.setBeActive(true);
            mdsMenu.setChildNumber(0);
            mdsMenu.setBeSystemProtect(true);
        }

//        filterCache.setFilterVersion();

        // 查询到旧的记录
        MdsMenu old = null;

        if (StrUtil.isNotBlank(mdsMenu.getId())) {

            old = super.getById(mdsMenu.getId());
        }

        if (StrUtil.isBlank(mdsMenu.getParentId())) {

            mdsMenu.setParentId(null);
        }

        super.store(mdsMenu);
        MdsRoleMenu mdsRoleMenu = new MdsRoleMenu();
        QueryWrapper<MdsRoleMenu> wrapper = new QueryWrapper<>();
        wrapper.eq("menu_id", mdsMenu.getId());
        wrapper.eq("role_id", "15444901512880128");
        MdsRoleMenu one = mdsRoleMenuService.getOne(wrapper);
        if (null == one) {
            mdsRoleMenu.setMenuId(mdsMenu.getId());
            mdsRoleMenu.setRoleId("15444901512880128");
            mdsRoleMenuService.saveOrUpdate(mdsRoleMenu);
        }
        this.settingsFullIds(old, mdsMenu);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void changeMenu(MdsMenuUpdateVO updateVO) {
        MdsMenu mdsMenu = EntityUtil.transform(updateVO, MdsMenu.class);
        MdsMenu oldMenu = null;
        if (StrUtil.isNotBlank(mdsMenu.getId())) {
            oldMenu = this.getById(updateVO.getId());
        }
        super.store(mdsMenu);
        // 新增 父节点 子节点数量 新增
        // 更新 如果切换 父节点 子节点减少
        this.settingsFullIds(oldMenu, mdsMenu);
    }

    /**
     * 更新父节点 子级数量等字段
     */
    private void settingsFullIds(MdsMenu old, MdsMenu mdsMenu) {
        if (old != null && StrUtil.isNotBlank(mdsMenu.getParentId()) && mdsMenu.getParentId().equals(old.getParentId())) {
            return;
        }
        MdsMenu parent = null;
        // 最顶级目录
        if (StrUtil.isNotBlank(mdsMenu.getParentId())) {
            // 根据父节点查询数据
            parent = super.getById(mdsMenu.getParentId(), true);
        }
        StringBuilder allParentIds = new StringBuilder();
        StringBuilder fullName = new StringBuilder();
        if (parent != null) {
            allParentIds.append(parent.getAllParentIds());
            allParentIds.append(MenuManagerConstant.CONTENT_SPLIT);
            fullName.append(parent.getFullName());
            fullName.append(MenuManagerConstant.CONTENT_SPLIT);
            // 当前选择的上一层级节点数加1
            parent.setChildNumber(parent.getChildNumber() + 1);
            super.store(parent);
        }
        allParentIds.append(mdsMenu.getId());
        mdsMenu.setAllParentIds(allParentIds.toString());
        fullName.append(mdsMenu.getName());
        mdsMenu.setFullName(fullName.toString());
        // 设置层级结构
        super.store(mdsMenu);
        if (old != null && StrUtil.isNotBlank(old.getParentId())) {
            MdsMenu oldParent = this.getById(old.getParentId());
            // 旧的父记录减1
            oldParent.setChildNumber(oldParent.getChildNumber() - 1);
            super.store(oldParent);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class, readOnly = false)
    public void changeMenuStatus(List<String> ids, boolean recordStatus) {
        List<MdsMenu> menuList = super.listByIds(ids);
        menuList.forEach(menu -> {
            HibaseAssert.isFalse(menu.getBeActive() == recordStatus,
                    "{} 重复操作",menu.getName());
            menu.setBeActive(recordStatus);
        });
        if (CollUtil.isNotEmpty(menuList)) {
            super.storeBatch(menuList);
        }
    }

    @Override
    public PageInfo<MdsMenu> selectMenuList(MdsMenuSearchVO searchVO) {

        QueryWrapper<MdsMenu> queryWrapper = new QueryWrapper<>();

        if (StrUtil.isNotBlank(searchVO.getCode())) {

            queryWrapper.eq("code", searchVO.getCode());
        }
        if (StrUtil.isNotBlank(searchVO.getName())) {

            queryWrapper.eq("name", searchVO.getName());
        }
        if (StrUtil.isNotBlank(searchVO.getType())) {

            queryWrapper.eq("type", searchVO.getType());
        }

        PageInfo<MdsMenu> result = super.page(searchVO, queryWrapper);

        return result;
    }

    @Override
    public List<MdsMenu> selectAllMenuList() {

        List<MdsMenu> result = new ArrayList<>();

        QueryWrapper<MdsMenu> wrapper = new QueryWrapper<>();

//        wrapper.ne("type", MenuType.MENU_TYPE_ELEMENT.getName());
        wrapper.eq("be_active", MenuManagerConstant.RECORD_STATUS_ENABLE);

        List<MdsMenu> Menus = super.list(wrapper);

        if (CollUtil.isNotEmpty(Menus)) {

            for (MdsMenu mdsMenu : Menus) {

                if (StrUtil.isBlank(mdsMenu.getParentId())) {

                    mdsMenu.setParentId(UserManagerConstant.MENU_TYPE_ROOT);
                }

                // 查找根目录下所有的菜单
                if (UserManagerConstant.MENU_TYPE_ROOT.equals(mdsMenu.getParentId())) {

                    result.add(mdsMenu);
                }
            }

            //为一级菜单设置子菜单准备递归
            for (MdsMenu menuBO : result) {

                //获取父菜单下所有子菜单调用recursionMenu
                List<MdsMenu> childList = recursionMenu(menuBO.getId(), Menus);
                menuBO.setMenuBODetail(childList);
                //menuBO.setId(menuBO.getCode());
            }
        }

        return result;
    }


    @Override
    public List<MdsMenuIndexVO> selectAllMenuGroupList() {
        List<MdsMenuIndexVO> mdsMenuIndexVOS = EntityUtil.transformByJson(selectAllMenuList(), MdsMenuIndexVO.class);

        List<MdsMenuIndexVO> mdsMenuGroupVOS = Lists.newArrayList();

        // 2-1.处理groupType不为null菜单
        List<MdsMenuIndexVO> groupTypeList = mdsMenuIndexVOS.stream().filter(x -> StrUtil.isNotEmpty(x.getGroupType())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(groupTypeList)) {
            Map<String, List<MdsMenuIndexVO>> groupMap = groupTypeList.stream().collect(Collectors.groupingBy(MdsMenuIndexVO::getGroupType));
            groupMap.forEach((key, items) -> {
                MdsMenuIndexVO groupVO = new MdsMenuIndexVO();
                groupVO.setId(UserManagerConstant.EXCLUDE_MENU_PREFIX + IdUtil.fastSimpleUUID());
                groupVO.setBeExcluded(true);
                groupVO.setName(key);
                groupVO.setGroupType(key);
                List<MdsMenuIndexVO> menuClientGroup = getMenuClientGroup(items);
                groupVO.setMenuBODetail(menuClientGroup);
                mdsMenuGroupVOS.add(groupVO);
            });

        }

        // 2-2.处理groupType为null菜单
        List<MdsMenuIndexVO> emptyGroupTypeList = mdsMenuIndexVOS.stream().filter(x -> StrUtil.isEmpty(x.getGroupType())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(emptyGroupTypeList)) {
            MdsMenuIndexVO groupVO = new MdsMenuIndexVO();
            groupVO.setId(UserManagerConstant.EXCLUDE_MENU_PREFIX + IdUtil.fastSimpleUUID());
            groupVO.setBeExcluded(true);
            groupVO.setName("group");
            groupVO.setGroupType("group");
            List<MdsMenuIndexVO> menuClientGroup = getMenuClientGroup(emptyGroupTypeList);
            groupVO.setMenuBODetail(menuClientGroup);
            mdsMenuGroupVOS.add(groupVO);
        }
        return mdsMenuGroupVOS;
    }

    /**
     * 获取分组菜单数据
     *
     * @param list 菜单列表
     * @return 分组菜单列表
     */
    private List<MdsMenuIndexVO> getMenuClientGroup(List<MdsMenuIndexVO> list) {
        if (CollUtil.isEmpty(list)) {
            return null;
        }

        List<MdsMenuIndexVO> clientGroup = Lists.newArrayList();

        // 2-1.处理clientType为Null的分组数据
        List<MdsMenuIndexVO> emptyGroup = list.stream().filter(menu ->
                StrUtil.isEmpty(menu.getClientType())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(emptyGroup)) {
            MdsMenuIndexVO tempVO = new MdsMenuIndexVO();
            tempVO.setId(UserManagerConstant.EXCLUDE_MENU_PREFIX + IdUtil.fastSimpleUUID());
            tempVO.setBeExcluded(true);
            tempVO.setName("group");
            tempVO.setClientType("group");
            tempVO.setMenuBODetail(emptyGroup);
            clientGroup.add(tempVO);
        }

        // 2-2.处理clientType不为为Null的分组数据
        List<MdsMenuIndexVO> group = list.stream().filter(menu ->
                StrUtil.isNotEmpty(menu.getClientType())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(group)) {
            // key: clientType
            Map<String, List<MdsMenuIndexVO>> clientTypeMap = group.stream().collect(
                    Collectors.groupingBy(MdsMenuIndexVO::getClientType));
            clientTypeMap.forEach((key, items) -> {
                MdsMenuIndexVO tempVO = new MdsMenuIndexVO();
                tempVO.setId(UserManagerConstant.EXCLUDE_MENU_PREFIX + IdUtil.fastSimpleUUID());
                tempVO.setBeExcluded(true);
                tempVO.setName(key);
                tempVO.setMenuBODetail(items);
                clientGroup.add(tempVO);
            });
        }

        return clientGroup;
    }

    /**
     * 查询角色绑定的菜单列表
     */
    @Override
    public List<MdsMenu> selectRoleMenuList(Map map) {

        return super.getBaseMapper().selectRootList(map);
    }

    /**
     * 查询数据权限
     *
     * @return
     */
    @Override
    public List<MdsDataPermDV> selectDataPerm(Map<String, Object> map) {

        return super.getBaseMapper().selectDataPerm(map);
    }

    @Override
    public void importMenu(MultipartFile multipartFile) throws IOException {
        log.info("【IMPORT-MENU】 导入生成menu | 文件名 : {}", multipartFile.getOriginalFilename());
        List<ImportMenuVO> importMenuVOS = new ExcelParse<ImportMenuVO>().parseExcel(ImportMenuVO.class, multipartFile.getInputStream());
        HibaseAssert.notEmpty(importMenuVOS, "service.wms.outbound.co.impl.WmsCoServiceImpl.importCoEta.dataIsNull");
        this.generateMenuByImport(importMenuVOS);
    }

    @Override
    public List<Tree<String>> treeMenu(MdsMenuTreeVO treeVO) {
        String parent = treeVO.getParentId() == null ? MdsMenuConstant.MENU_TREE_ROOT_ID : treeVO.getParentId();
        List<TreeNode<String>> collect = baseMapper
                .selectList(
                        Wrappers.<MdsMenu>lambdaQuery().like(StrUtil.isNotBlank(treeVO.getName()),
                                        MdsMenu::getName, treeVO.getName())
                                .orderByAsc(MdsMenu::getSort))
                .stream().map(getNodeFunction()).collect(Collectors.toList());
        // 模糊查询 不组装树结构 直接返回 表格方便编辑
        if (StrUtil.isNotBlank(treeVO.getName())) {
            return collect.stream().map(node -> {
                Tree<String> tree = new Tree<>();
                tree.putAll(node.getExtra());
                BeanUtils.copyProperties(node, tree);
                return tree;
            }).collect(Collectors.toList());
        }
        return TreeUtil.build(collect, parent);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addAll(String id) {
        List<MdsMenu> menuList = super.list();
        List<MdsRoleMenu> roleMenuList = new ArrayList<>();
        menuList.forEach(menu -> {
            MdsRoleMenu roleMenu = new MdsRoleMenu();
            roleMenu.setRoleId(id);
            roleMenu.setMenuId(menu.getId());
            roleMenuList.add(roleMenu);
        });
        mdsRoleMenuService.storeBatch(roleMenuList);
    }

    @NotNull
    private Function<MdsMenu, TreeNode<String>> getNodeFunction() {
        return menu -> {
            TreeNode<String> node = new TreeNode<>();
            node.setId(menu.getId());
            node.setName(menu.getName());
            node.setParentId(menu.getParentId());
            node.setWeight(menu.getSort());
            // 扩展属性
            Map<String, Object> extra = new HashMap<>();
            extra.put("code", menu.getCode());
            extra.put("icon", menu.getIcon());
            extra.put("sort", menu.getSort());
            extra.put("type", menu.getType());
            extra.put("url", menu.getUrl());
            extra.put("trueUrl", menu.getTrueUrl());
            node.setExtra(extra);
            return node;
        };
    }

    private void generateMenuByImport(List<ImportMenuVO> importMenuVOS) {
        // step 1. 根据parentCode 分类
        Map<String, List<ImportMenuVO>> collect = importMenuVOS.stream().collect(Collectors.groupingBy(ImportMenuVO::getParentCode));

        collect.forEach((key, value) -> {
            // step 2. 获取父菜单
            MdsMenu parentMenu = this.getOne(new QueryWrapper<MdsMenu>().eq("code", key));
            HibaseAssert.notNull(parentMenu, "未找到父菜单:{}", key);
            value.forEach(p -> {
                MdsMenu menu = this.getOne(new QueryWrapper<MdsMenu>().eq("code", p.getCode()));
                if (menu == null) {
                    log.info("【IMPORT-MENU】 导入新增菜单: {}", p);
                    this.changeMenu(new MdsMenuUpdateVO() {{
                        setCode(p.getCode());
                        setParentId(parentMenu.getId());
                        setType(p.getType());
                        setName(p.getName());
                        setUrl(p.getUrl());
                    }});
                } else {
                    log.info("【IMPORT-MENU】 导入菜单已存在，更新URL。 {}", p);
                    menu.setUrl(p.getUrl());
                    this.store(menu);

                }


            });
        });
    }
}
